//
//  ATMPPRBasePDENoritsu.m
//  PDE
//
//  Copyright 2007, 2008 Atomic Pepper LLC. All rights reserved.
//
//  Triprism Inc., is hereby granted a worldwide, perpetual, fully
//  paid license to use, modify, copy, compile and sublicense the
//  contents of this file.

#import "ATMPPRBasePDE.h"
#include <notify.h>
@class VMUSymbolicator;
@class ATMPPR_stepper;
static void catchPIPE(int sigraised)
{
    CUPS_DEBUG("caught a signal %d\n",sigraised);
	//    kill(getpid(), SIGBUS);
}
@interface ATMPPRBasePDEDNP (privateMethods)
- (void)setPanelView:(NSView *)panel;
- (CFStringRef)printerDeviceURI;
void initializeContext(usbtbTaskContext * jobContext);
void clearUsbtbContextData(usbtbTaskContext *jobContext);
void destroyJobContext(usbtbTaskContext * jobContext);
- (int) performTaskwithUsbtbWithArgs:(NSArray *) args andContext:(usbtbTaskContext *)jobContext;
@end

// Class globals
NSSpeechSynthesizer * _gClass_speaker = nil;
static NSMutableDictionary * _rootPrefsDict;
static NSString * rootPrefsDictPath = @"thisObjectCurrentPath";

@implementation ATMPPRBasePDEDNP
{
    BOOL                    pdePluginDidUnload;
    dispatch_semaphore_t    pdeUnloadSemaphore;
}
@synthesize paperPopup = _paperPopup;
+(void)threadedSpeech:(id)obj
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    _gClass_speaker = [[NSSpeechSynthesizer alloc] init];
    [_gClass_speaker startSpeakingString:@"Welcome!"];
    [pool drain];
}
+(void)initialize
{
//    [NSThread detachNewThreadSelector:@selector(threadedSpeech:) toTarget:self withObject:nil];
//    _gClass_speaker = [[NSSpeechSynthesizer alloc] init];
//    [_gClass_speaker startSpeakingString:@"Welcome!"];
//    [[self speaker] startSpeakingString:@"Welcome!"];
    _rootPrefsDict = nil;
}
+(NSSpeechSynthesizer *)speaker
{
    if(nil == _gClass_speaker)
    {
        _gClass_speaker = [[NSSpeechSynthesizer alloc] init];
    }
    return _gClass_speaker;
}
-(NSSpeechSynthesizer *)speaker
{
    return [[self class] speaker];
}
-(void)speakString:(NSString *)string
{
    if(NO)
    {
        [[self speaker] startSpeakingString:string];
    }
}
- (NSArray *)searchView:(NSView *)view forInstancesOfClass:(Class)class
{
    //    NSView * sv;
    //    while(sv != (sv = _panelView.superview))
    //    {
    //        db("superview %@ with subviews:%@",sv,sv.subviews);
    //    }
    NSMutableArray * m_a = [NSMutableArray array];
    for (id subview in view.subviews)
    {
        if([subview isKindOfClass:class])
        {
            [m_a addObject:subview];
        }
        [m_a addObjectsFromArray:[self searchView:subview forInstancesOfClass:class]];
    }
    return m_a;
}
-(void)noted:(NSNotification *)note
{
    return;
//    NSString * name = note.name;
//    if([name isEqualToString:@"NSMenuDidRemoveItemNotification"]
//       ||[name isEqualToString:@"NSMenuDidAddItemNotification"]
//       ||[name isEqualToString:@"NSMenuDidChangeItemNotification"]
//       ||[name isEqualToString:@"NSMenuDidSendActionNotification"]
//       ||[name isEqualToString:@"NSMenuDidCompleteInteractionNotification"]
//       ||[name isEqualToString:@"NSMenuDidBeginTrackingNotification"]
//       ||[name isEqualToString:@"NSMenuDidEndTrackingNotification"]
//
//       ||[name isEqualToString:@"NSApplicationWillUpdateNotification"]
//       ||[name isEqualToString:@"NSApplicationDidUpdateNotification"]
//       ||[name isEqualToString:@"NSApplicationWillResignActiveNotification"]
//       ||[name isEqualToString:@"NSApplicationDidResignActiveNotification"]
//       ||[name isEqualToString:@"NSApplicationWillBecomeActiveNotification"]
//       ||[name isEqualToString:@"NSApplicationDidBecomeActiveNotification"]
//
//       ||[name isEqualToString:@"NSWindowDidUpdateNotification"]
//       ||[name isEqualToString:@"NSWindowDidResignKeyNotification"]
//       ||[name isEqualToString:@"NSWindowDidResignMainNotification"]
//       ||[name isEqualToString:@"NSWindowDidBecomeMainNotification"]
//       ||[name isEqualToString:@"NSWindowDidBecomeKeyNotification"]
//
//       ||[name isEqualToString:@"NSThreadWillExitNotification"]
//       ||[name isEqualToString:@"_NSThreadDidStartNotification"]
//
//       ||[name isEqualToString:@"NSViewDidUpdateTrackingAreasNotification"]
//       ||[name isEqualToString:@"NSViewFrameDidChangeNotification"]
//
//       ||[name isEqualToString:@"NSPopUpButtonWillPopUpNotification"]
//       ||[name isEqualToString:@"NSPopUpButtonCellWillPopUpNotification"]
//       )
//    {
//        return;
//    }
//    id object = note.object;
//    NSDictionary * info = note.userInfo;
//    Class c = [object class];
//    db("note on object %@ with name %@ and info %@",object,name,info);
//    while(c != [NSObject class])
//    {
//        name = NSStringFromClass(c);
//        c = [c superclass];
//    }
 //PMPDEController
    //@"kInkCheckerNotification"
    //@"PMPrintHideDuplexControlsNotification"
    //@"PMPrintDestinationPaperDidChangeNotification"
    //@"PMPrintSuggestedPaperDidChangeNotification"
    //@"PMPrinterChangedNotification"
}
- (NSString *)cupsDestination
{
    NSString * destName = (NSString*)PMPrinterGetID([pdePluginCallbackObject PMPrinter]);
    return destName;
}
- (id)initWithCallback:(id)callback
{
	if(self = [super init])
    {
        pdePluginCallbackObject = [callback retain];

        // register for "cupsDidUpdatePPD" notifications
        notify_register_dispatch(self.cupsDestination.UTF8String, &_notifyToken, dispatch_get_main_queue(), ^(int token) {
            
            // this block is called whenever the commandFilter signals an update by notifyd
            [self ppdDidUpdate];
        });
        
//        if (signal(SIGPIPE, catchPIPE) == SIG_ERR)
//        {
//            CUPS_DEBUG("error registering for SIGPIPE handler\n");
//        }
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(noted:) name:nil object:nil];
        // init instance variables
        _panelView = nil;
        _paperPopup = nil;
        supportedPPDOptionKeys = nil;
        choiceDict = [[NSMutableDictionary alloc] initWithCapacity:[[self supportedPPDOptionKeys] count]];
        thePDEName = nil;
        ppd = nil;
        streamIN = nil;
        streamERR = nil;
        task = nil;
        deviceURI = NULL;

        pdePluginDidUnload = NO;
        pdeUnloadSemaphore = NULL;

        
        // Obtain the bundle for use in translating strings.
        pdeBundle = [[NSBundle bundleForClass:[self class]] retain];
            
        // This test whether the constraint handling support is in place, i.e. 10.5 and later. 
        useConstraintHandling = [ pdePluginCallbackObject respondsToSelector:@selector(willChangePPDOptionKeyValue:ppdChoice:) ];

//        Boolean didValidate;
//        PMSessionValidatePageFormat([pdePluginCallbackObject printSession], [pdePluginCallbackObject pageFormat], &didValidate);

        if(useConstraintHandling)
        {
            ppd = [pdePluginCallbackObject ppdFile];
        }
        else
        {
            ppd = [self getPPDOnTiger ];
    #if __LP64__
            [self panelView];
    #else
            @try
            {
                [self panelView];
            }
            @catch(NSException *e)
            {
                NSLog(@"caught an exception %@",e);
            }
    #endif
        }

        if(ppd)
        {
//            [self setDefaults];
            
    //		// Register the help book.
    //		FSRef fsref;
    //		NSString * path = [pdeBundle bundlePath];
    //		NSURL * url = [NSURL fileURLWithPath:path];
    //		if(CFURLGetFSRef((CFURLRef)url, &fsref))
    //		{
    //			OSStatus err = AHRegisterHelpBook(&fsref);
    //			if(err)
    //				NSLog(@"TP PDE got an error %d registering its help book!", err);
    //		}
        }
        else
        {
            // Error getting the PPD so we cannot continue.
            [self release];
            self = nil;
        }
	}
//    [self locatePP];
//    [self performSelector:@selector(setPaper) withObject:nil afterDelay:0.05];
	return self;
}
-(BOOL)restore:(id)p from:(id)t
{
    BOOL didRestore = NO;
    if ([p isKindOfClass:[NSPopUpButton class]] && [t isKindOfClass:[NSString class]])
    {
        NSPopUpButton * popup = p;
        NSMenuItem * item = [popup itemWithTitle:t];
        if (item)
        {
            [popup selectItem:item];
            [item.target performSelector:item.action withObject:item];
            didRestore = YES;
        }
    }
    return didRestore;
}
-(void)setPaper
{
    if(self.paperPopup)
    {
        if (![self restore:self.paperPopup from:@"6x8"])
        {
            [self performSelector:@selector(setPaper) withObject:nil afterDelay:0.05];
        }
    }
//    NSPopUpButton * popup = self.paperPopup;
//    NSMenuItem * item = [popup itemWithTitle:@"6x8"];
//    NSString * selectorName = NSStringFromSelector([popup action]);
//    NSString * title = popup.title;
//    for (NSMenuItem * item in popup.itemArray)
//    {
//        if(item.hasSubmenu)
//        {
//            for (NSMenuItem * subItem in item.submenu.itemArray) {
//                selectorName = NSStringFromSelector([subItem action]);
//                title = subItem.title;
//            }
//        }
//        selectorName = NSStringFromSelector([item action]);
//        title = item.title;
//        if([title isEqualToString:@"6x8"])
//        {
//            [popup selectItem:item];
//            
//            [item.target performSelector:item.action withObject:item];
//            return;
//        }
////        id ro = item.representedObject;
////        BOOL enabled = item.isEnabled;
//        
//    }
//    [self performSelector:@selector(setPaper) withObject:nil afterDelay:0.05];
}
-(void)locatePP
{
    NSArray * popups =
    [self searchView:[NSApp keyWindow].contentView forInstancesOfClass:NSPopUpButton.class ];
    for (NSPopUpButton * popup in popups)
    {
        NSString * selectorName = NSStringFromSelector([popup action]);
        NSString * title = popup.title;
        for (NSMenuItem * item in popup.itemArray)
        {
            selectorName = NSStringFromSelector([item action]);
            if(item.hasSubmenu)
            {
                for (NSMenuItem * subItem in item.submenu.itemArray)
                {
                    selectorName = NSStringFromSelector([subItem action]);
                    if(subItem.action == @selector(changePaperSize:))
                    {
                        self.paperPopup = popup;
                        return;
                    }
                    title = subItem.title;
                }
            }
            else if(item.action == @selector(changePaperSize:))
            {
                self.paperPopup = popup;
                return;
            }
            //selectorName	__NSCFString *	@selector(changePaperSize:)
//            selectorName = NSStringFromSelector([item action]);
//            title = item.title;
//            if([title isEqualToString:@"6x8"])
//            {
//                [popup selectItem:item];
//                break;
//            }
//            id ro = item.representedObject;
//            BOOL enabled = item.isEnabled;
            
        }
    }
}
/* The dealloc method is called on object deallocation when garbage collection is not in effect.
    Therefore this is the place to release objects that we've allocated or retained.
*/
//+(NSString *)backtrace:(NSUInteger)count
//{
//
//    system([[NSString stringWithFormat:@"/usr/libexec/stackshot -i -t -p %d",[[NSProcessInfo processInfo] processIdentifier]] UTF8String]);
//    [[NSWorkspace sharedWorkspace] openFile:@"/Library/Logs/stackshot-syms.log"];
//    [[NSWorkspace sharedWorkspace] openFile:@"/Library/Logs/stackshot.log"];
//    NSMutableString *mString = nil;
//    intptr_t * * stackAddresses;
//    stackAddresses = malloc(sizeof(void *) * count);
//    if (stackAddresses)
//    {
//        mString = [NSMutableString string];
//        count = backtrace((void **)stackAddresses, count);
//        char ** symbols = backtrace_symbols((void **)stackAddresses, count);
//        task_t task;
//        VMUSymbolicator * symbolicator;
//        
//        int perr = task_for_pid(mach_task_self(), [[NSProcessInfo processInfo] processIdentifier], &task);
//        if (perr == KERN_SUCCESS)
//        {
//            symbolicator = [VMUSymbolicator symbolicatorForTask:task];
//        }
//        else
//        {
//            symbolicator = [VMUSymbolicator symbolicatorForPid:[[NSProcessInfo processInfo] processIdentifier]];
//        }
//        int i;
//        for( i=0 ; count > i ; i++ )
//        {
//            [mString appendFormat:@"%@ %p %s\n",[symbolicator symbolForAddress:((intptr_t *)stackAddresses)[i]], ((intptr_t *)stackAddresses)[i], symbols[i]];
//        }
//        if(symbols)
//        {
//            free(symbols);
//        }
//    }
//    
//    return [[mString copy] autorelease];
//}
- (void)dealloc
{
    notify_cancel(_notifyToken);

    pdePluginDidUnload = YES;
    
    if(NULL != pdeUnloadSemaphore)
    {
        // signal that we are unloading
        dispatch_semaphore_t tempSem = pdeUnloadSemaphore;
        // calling wait with a time of now decrements the semaphore and returns immediately
        dispatch_semaphore_wait(tempSem, DISPATCH_TIME_FOREVER);
        NSLog(@"continuing dealloc after waiting for unload semaphore");
        dispatch_release(tempSem);
    }
    
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    ppd = nil;
    [self setPanelView:nil];
	[ pdePluginCallbackObject release ];
    pdePluginCallbackObject = nil;
	[ pdeBundle release ];
    pdeBundle = nil;
	[ thePDEName release ];
	[choiceDict release];
    choiceDict = nil;
	[supportedPPDOptionKeys release];

    DESTROY_CFTypeRef(deviceURI);
	

	[super dealloc];
}


/*
    The finalize method is only needed when you have resources or data that
    needs to be released when garbage collection is in effect. Since the NSObjects
	represented by our instance variables are garbage collected, there is no
	need to release them in the finalize method. Since this code example has no
	other resources that need to be deallocated when garbage collection is in effect,
	there is no need for a finalize method in this code.
	
	If your code needs to explicitly deallocate resources that are not garbage
	collected even when garbage collection is in place, remove the ifdef here and add
	your needed deallocations prior to calling the superclass'es finalize.
*/
#if 0
- (void)finalize {
    // Release your resources that are not garbage collected.
    // ...
    
    // Then finalize the superclass.
    [super finalize];
}
#endif

#pragma mark -- PDEPanel custom methods
- (CFStringRef)printerDeviceURI
{
    if(NULL == deviceURI)
    {
        PMPrinter printer = [pdePluginCallbackObject PMPrinter];
        CFURLRef dUriURL=NULL;
        CFURLRef tmpURL=NULL;
        if(printer)
        {
            PMPrinterCopyDeviceURI([pdePluginCallbackObject PMPrinter], &dUriURL);
        }
        if(NULL != dUriURL)
        {
            if((tmpURL=CFURLGetBaseURL(dUriURL)))
            {
                CFURLRef tmpURL = CFURLCopyAbsoluteURL(dUriURL);
                CFRelease(dUriURL);
                dUriURL = tmpURL;
            }
            deviceURI = CFURLGetString(dUriURL);
            if(NULL != deviceURI)
            {
                deviceURI = CFRetain(deviceURI);
            }
            DESTROY_CFTypeRef(dUriURL);
        }
    }
    return deviceURI ? : CFSTR("usb://");
}

#pragma mark  Preferences
+(NSString *)customPrefsFilename
{
    NSBundle * b = [NSBundle mainBundle];
    b = [NSBundle  bundleWithPath:@"/Library/Printers/DNP-RX/DNPPrinterDialog.bundle"];
    return [[b bundleIdentifier] stringByAppendingPathExtension:@"plist"];
}
+(id)globalRootPrefsDictionary
{
    if (nil == _rootPrefsDict)
    {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
        NSDictionary * plist = nil;
        NSString * path;
        NSString * basePath = [@"Preferences" stringByAppendingPathComponent:[self customPrefsFilename]];
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, NO);
        NSEnumerator * e = [paths objectEnumerator];
        db("local dirs = %@", paths);
        while (nil == plist && ( path = [e nextObject] ) )
        {
            path = [path stringByAppendingPathComponent:basePath];
            plist = [NSDictionary dictionaryWithContentsOfFile:path];
        }
        if(plist)
        {
            _rootPrefsDict = [plist mutableCopy];
        }
        else
        {
            _rootPrefsDict = [[NSMutableDictionary alloc] initWithCapacity:14];
        }
        NSFileManager * fm = [NSFileManager defaultManager];
        if(nil == path || NO ==[fm isWritableFileAtPath:path])
        {
            path = [paths objectAtIndex:0] ? : NSTemporaryDirectory();
            path = [path stringByAppendingPathComponent:basePath];
        }
        db( "using global prefs path %@", path );
        [_rootPrefsDict setObject:path forKey:rootPrefsDictPath];
        [pool drain];
    }
    
    return _rootPrefsDict;
}
+(NSDictionary *)globalPrefsDictionaryForDevice:(id)device
{
    return [[self globalRootPrefsDictionary] objectForKey:device];
}
-(NSDictionary *)globalPrefsDictionary
{
    return [[self class] globalPrefsDictionaryForDevice:(id)[self printerDeviceURI]];
}
+(void)saveToGlobalPrefsDictionaryValue:(id)value forKey:(NSString *)key device:(id)device
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    id devicePrefs = [self globalPrefsDictionaryForDevice:device];
    if([[devicePrefs objectForKey:key] isEqual:value] || nil == key)
    {
        return;
    }
    
    if(nil == devicePrefs)
    {
        devicePrefs = [NSMutableDictionary dictionaryWithCapacity:14];
    }
    else if([devicePrefs isKindOfClass:[NSDictionary class]] && NO == [devicePrefs respondsToSelector:@selector(setObject:forKey:)])
    {
        devicePrefs = [devicePrefs mutableCopy];
    }
    
    if(value)
    {
        [devicePrefs setObject:value forKey:key];
    }
    else
    {
        [devicePrefs removeObjectForKey:key];
    }
    
    //save it out
    NSMutableDictionary * mRoot = [self globalRootPrefsDictionary];
    [mRoot setObject:devicePrefs forKey:device];
    
    NSString * path = [mRoot objectForKey:rootPrefsDictPath];
    if(NO == [mRoot writeToFile:path atomically:YES])
    {
        db( "failed to save global preferences to %@", path );
    }
    [pool drain];
}
-(void)saveToGlobalPrefsDictionaryValue:(id)value forKey:(NSString *)key
{
    [[self class] saveToGlobalPrefsDictionaryValue:value forKey:key device:(id)[self printerDeviceURI]];
}
#pragma mark PPD methods
-(NSDictionary *)ppdValuesForAttributes:(NSArray *)keys
{
	NSString * choice = nil;

    //we may need to update our PPD file ref if we are looking for updates from PMPrinterSendCommand
    const char * dest = self.cupsDestination.UTF8String;
    const char * ppdFile;
    NSMutableDictionary * m_d = [NSMutableDictionary dictionaryWithCapacity:keys.count];
    if (keys.count && m_d && dest && (ppdFile = cupsGetPPD(dest)))
    {
        ppd_file_t * freshPPD = ppdOpenFile(ppdFile);

        if( freshPPD )
        {
            //ppd_option_t * option;
            ppd_attr_t * attr;
            //        if( (option = ppdFindOption( ppd, [key UTF8String]) ) && (option->defchoice) )
            //        {
            //            choice = [NSString stringWithUTF8String:option->defchoice ];
            //        }
            //        else
            for (NSString * key in keys)
            {
                if( (attr = ppdFindAttr(freshPPD, [key UTF8String], NULL)) && attr->value)
                {
                    choice = [NSString stringWithUTF8String:attr->value];
                    [m_d setObject:choice forKey:key];
                }
            }
            
            ppdClose(freshPPD);
            unlink(ppdFile);
        }
    }
    return [m_d.copy autorelease];
}
-(NSString *)ppdValueForAttribute:(NSString *)key
{
    return [[self ppdValuesForAttributes:[NSArray arrayWithObject:key]] objectForKey:key];
}
//
////    db("got a ppd at %s for printer %s",ppdFile,dest);
//    //ppdOpenFile(ppdFile);
//    //ppdClose(ppd);
//    //unlink(ppdFile);
//    //[pdePluginCallbackObject PMPrintSession]
//    if( ppd && key )
//    {
//        //ppd_option_t * option;
//        ppd_attr_t * attr;
////        if( (option = ppdFindOption( ppd, [key UTF8String]) ) && (option->defchoice) )
////        {
////            choice = [NSString stringWithUTF8String:option->defchoice ];
////        }
////        else
//        if( (attr = ppdFindAttr(ppd, [key UTF8String], NULL)) && attr->value)
//        {
//            choice = [NSString stringWithUTF8String:attr->value];
//        }
//    }
//    return choice;
//}

- (NSString *) ppdOptionKeyForTag:(int)tag
{
    //subclasses must override
	NSString *key=nil;
	switch(tag)
	{
	default:
		break;
	}
	return key;
}
-(void)populateMenuObjectsInView:(id)view
{
    // find the menu objects in this view
    // get the ppdoption for each object
    // get the
    NSInteger tag=0;
    NSInteger i;
    NSPopUpButton * popup;
    ppd_option_t * option;
    NSString *optionKey;
    NSString *choice=nil;
    NSString *text=nil;
    //db("trying to translate popup item  \"%@\" to \"%@\"", choice, text);

    while((popup=[self view:view controlForTag:tag++]))
    {
        if(NO == [popup isKindOfClass:[NSPopUpButton class]] || nil == (optionKey = [self ppdOptionKeyForTag:[popup tag]]) || NULL == (option = ppdFindOption(ppd, [optionKey UTF8String])))
        {
            continue;
        }
        
        [popup removeAllItems];
        for(i = 0; option->num_choices > i ; ++i)
        {
            choice=[NSString stringWithUTF8String:option->choices[i].choice];
            text = NSLocalizedStringFromTableInBundle(choice, nil, pdeBundle, nil);
            //db("trying to translate popup item  \"%@\" to \"%@\"", choice, text);
            if( [text isEqualToString:choice] || 0 == [text length] )
            {
                // fall back to the PPD translation
                text = NSLocalizedStringFromTableInBundle([NSString stringWithUTF8String:option->choices[i].text], nil, pdeBundle, nil);
            }
            [popup addItemWithTitle:text];
            [[popup itemAtIndex:i] setRepresentedObject:choice];
        }
        [popup selectItemAtIndex:[popup indexOfItemWithRepresentedObject:[NSString stringWithUTF8String:option->defchoice]]];
        [popup synchronizeTitleAndSelectedItem];
    }    
}
-(void)enableTextFieldsForEditingInView:(id)view
{
    int tag=kTPPDEControlValueMask;
    id object;
    while((object=[self view:view controlForTag:tag++]) || (object=[self view:view controlForTag:tag++] ))
    {
        NSTextField * textField = object;
        [textField setTarget:self];
        [textField setDelegate:self];
        [[textField cell] setBackgroundColor:[NSColor whiteColor]];
        [textField setAction:@selector(changePrinterSetting:)];
        [textField setEditable:YES];
    }
}
-(BOOL)localizeStringsInView:(id)view andSubviews:(BOOL)also resizeTofit:(BOOL)shouldResize
{
    NSString *current = nil;
    NSString *local = nil;
    SEL setStringSelector;

    BOOL didLocalizeStrings = NO;
    BOOL toolTipsInCells = NO;
    NSArray *subs=nil;
    NSEnumerator *subE;
    id subview;

    if([view respondsToSelector:@selector(tabViewItems)])
    {
        for (NSTabViewItem * item in [view tabViewItems])
        {
            if(also)
            {
                didLocalizeStrings = [self localizeStringsInView:item.view andSubviews:YES resizeTofit:shouldResize] || didLocalizeStrings;
            }
            
            current = item.label;
            if (current && (local = [pdeBundle localizedStringForKey:current value:@"" table:nil]))
            {
                [item setLabel:local];
            }
        }
    }
    else if(also)
    {
        if([view respondsToSelector:@selector(cells)])
        {
            subs=[view cells];
            
            if([view respondsToSelector:@selector(toolTipForCell:)] && [view respondsToSelector:@selector(setToolTip: forCell:)])
            {
                toolTipsInCells = YES;
            }
        }
        else if([view respondsToSelector:@selector(subviews)])
        {
            subs=[view subviews];
        }
        if(subs)
        {
            subE = [subs objectEnumerator];
            while(subview = [subE nextObject])
            {
                didLocalizeStrings = [self localizeStringsInView:subview andSubviews:YES resizeTofit:shouldResize] || didLocalizeStrings;
                if(toolTipsInCells)
                {
                    current = [view toolTipForCell:subview];
                    if([current length] && (local = [pdeBundle localizedStringForKey:current value:@"" table:nil]) && NO == [local isEqualToString:current])
                    {
                        [view setToolTip:local forCell:subview];
                    }
                }
            }
        }
    }
    
    current = nil;
    //now check this view to see if it needs localizing
    if([view respondsToSelector:@selector(title)] && [view respondsToSelector:@selector(setTitle:)])
    {
        setStringSelector = @selector(setTitle:);
        current = [view title];
    }
    else if([view respondsToSelector:@selector(stringValue)] && [view respondsToSelector:@selector(setStringValue:)])
    {
        setStringSelector = @selector(setStringValue:);
        current = [view stringValue];
    }

    if(current && (local = [pdeBundle localizedStringForKey:current value:@"" table:nil]) && NO == [local isEqualToString:current])
    {
        
        [view performSelector:setStringSelector withObject:local];
        didLocalizeStrings = YES;
        if(shouldResize && [view respondsToSelector:@selector(sizeToFit)])
        {
            [view sizeToFit];
        }
    }

    if([view respondsToSelector:@selector(toolTip)] && [view respondsToSelector:@selector(setToolTip:)])
    {
        current = [view toolTip];
        if([current length] && (local = [pdeBundle localizedStringForKey:current value:@"" table:nil]) && NO == [local isEqualToString:current])
        {
            [view setToolTip:local];
        }
    }

    return didLocalizeStrings;
}
- (void) setChoice:(NSString *)choice forKey:(NSString *)key
{
	if (nil == choiceDict)
	{
		choiceDict = [[NSMutableDictionary dictionaryWithCapacity:[[self supportedPPDOptionKeys] count]] retain];
	}
	
	if(choice && key)
	{
		[choiceDict setObject:choice forKey:key];
	}
}
- (NSString *) ppdChoiceForTag:(int)tag
{
    //subclasses must override
	return nil;
}
- (id)newChoiceForSender:(id)sender
{
    NSString *choice=nil;
    if([sender isKindOfClass:[NSPopUpButton class]]) {
        choice = [[sender selectedItem] representedObject];
    }
    else if([sender isKindOfClass:[NSMatrix class]]) {
        NSInteger tag = 0;
        if([sender respondsToSelector:@selector(selectedCell)])
        {
            tag = [[sender selectedCell] tag];
        }
        else if([sender respondsToSelector:@selector(tag)])
        {
            tag = [sender tag];
        }
        choice =  [self ppdChoiceForTag:tag];
    }
    else if([sender isKindOfClass:[NSTextField class]]) {
        if(NO == [choice = [sender stringValue] isEqualToString:@"-"])
        {
            choice = [choice stringByTrimmingCharactersInSet:[[NSCharacterSet characterSetWithCharactersInString:@"-0123456789"] invertedSet]];
            choice = [NSString stringWithFormat:@"%d",[choice intValue]];
        }
//        if(NO == [choice = [sender stringValue] isEqualToString:@"-"])
//        {
//            choice = [NSString stringWithFormat:@"%d",[sender intValue]];
//        }
    }
    else if ([sender isKindOfClass:[NSButton class]]) {
        BOOL isEnabled = [sender state];
        choice = isEnabled ? @"True" : @"False";
    }
    else if ([sender isKindOfClass:[NSSlider class]]) {
        choice = @([sender integerValue]).stringValue;
    }
    else
    {
        choice = [sender stringValue];
    }
    return choice;
}
-(void)setToolTip:(id)value fromSender:(id)sender
{
	[sender setToolTip:value];
}
-(void)setGlobalDefaultsObject:(id)object forKey:(id)key
{
    if(object && key)
    {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        NSDictionary * gd = [[NSUserDefaults standardUserDefaults] persistentDomainForName:NSGlobalDomain];
        NSMutableDictionary *mGd = [[gd mutableCopy] autorelease];
        [mGd setObject:object forKey:key];
        [[NSUserDefaults standardUserDefaults] setPersistentDomain:[[mGd copy] autorelease] forName:NSGlobalDomain];
        [pool drain];
    }
}
-(NSString *)floatValueForSender:(id)sender format:(NSString *)fmt allowsNegative:(BOOL)allowsNegative
{
    NSString *string = nil;
    if([sender respondsToSelector:@selector(stringValue)])
    {
        string = [sender stringValue];
    }
    else if([sender respondsToSelector:@selector(floatValue)])
    {
        string = [[NSNumber numberWithFloat:[sender floatValue]] stringValue];
    }
    if( [string length] )
    {
        string = [string stringByTrimmingCharactersInSet:[[NSCharacterSet characterSetWithCharactersInString: allowsNegative ? @".0123456789-" : @".0123456789"] invertedSet]];
    }
    string = [NSString stringWithFormat:fmt, [string doubleValue]];
    return string;
}
-(BOOL)validateChoice:(NSString *)choice forPPDOptionKey:(NSString *)ppdOptionKey fromSender:(id)sender
{
    BOOL isValid = YES;
    if([sender isKindOfClass:[NSTextField class]])
    {
        NSInteger tag = [sender tag];
        double max=0;
        double min=0;
        double val = [choice doubleValue];
        NSInteger ticks = 0;
        if(kTPPDEControlValueMask & tag)
        {
            tag ^= kTPPDEControlValueMask;
        }
        id control = [self view:[sender superview] controlForTag:tag];
        if([control respondsToSelector:@selector(maxValue)] || [control respondsToSelector:@selector(minValue)])
        {
            if([control respondsToSelector:@selector(maxValue)])
            {
                max = [control maxValue];
            }
            if([control respondsToSelector:@selector(minValue)])
            {
                min = [control minValue];
            }
            if([control respondsToSelector:@selector(numberOfTickMarks)])
            {
                ticks = [control numberOfTickMarks];
            }
            else if ([control respondsToSelector:@selector(increment)])
            {
                ticks = 1 + ( (max - min) / [control increment] );
            }
            if(val > max || val < min)
            {
                isValid = NO;
            }
            else
            {
                if(ticks)
                {
                    ticks--;
                    val *= ticks;
                    max = (max - min)/ticks;
                    
                    
                }
            }
        }
    }
    
    return isValid;
}
-(void)rejectChoice:(NSString *)choice forPPDOptionKey:(NSString *)ppdOptionKey fromSender:(id)sender
{
    NSBeep();
    [self speakString:[NSString stringWithFormat:@"rejecting choice %@ for %@", choice, ppdOptionKey]];
    
    choice = [choiceDict objectForKey:ppdOptionKey];

    if(useConstraintHandling)
    {
        ppd_choice_t * ppdChoice = ppdFindMarkedChoice(ppd, [ppdOptionKey UTF8String]);
        if(ppdChoice)
        {
            choice = [NSString stringWithUTF8String:ppdChoice->choice];
        }
    }

    [self updateControl:sender withValue:choice];
}
-(NSString *)displayFormatForString:(NSString*)val forPPDOptionKey:(NSString *)ppdOptionKey
{
    return val;
}
-(BOOL)markChoice:(NSString *)choice forPPDOptionKey:(NSString *)ppdOptionKey fromSender:(id)sender
{
    BOOL accepted = (nil != choice && nil != ppdOptionKey);
    
    if(accepted && [self validateChoice:choice forPPDOptionKey:ppdOptionKey fromSender:sender])
    {
        if(useConstraintHandling)
        {    
            if((accepted=[pdePluginCallbackObject willChangePPDOptionKeyValue:ppdOptionKey ppdChoice: choice]))
            {
                [self setChoice:choice forKey:ppdOptionKey];
            }
            else
            {
                //NSBeep();
                //NSLog(@"The print system rejected the user selected choice %@ for PPD Option %@", choice, ppdOptionKey);
//                [[NSAlert alertWithMessageText:@"setting change failed!" defaultButton:@"Bummer!" alternateButton:nil otherButton:nil informativeTextWithFormat:@"I tried to make the change you requested but the printing system rejected my efforts. Sorry."] runModal];
                // the print system rejected the setting so set the UI to reflect the previous value
                ppd_choice_t * ppdChoice = ppdFindMarkedChoice(ppd, [ppdOptionKey UTF8String]);
                [self setChoice:[NSString stringWithUTF8String:ppdChoice->choice] forKey:ppdOptionKey];
                [self performSelector:@selector(updateUI) withObject:nil afterDelay:0.01];
                //[self updateControl:sender withValue:[NSString stringWithUTF8String:ppdChoice->choice]];
            }
        }
        else
        {
            [self setChoice:choice forKey:ppdOptionKey];
        }
        if(accepted && nil != sender)
        {
            choice = [self displayFormatForString:choice forPPDOptionKey:ppdOptionKey];
            [self updateControl:[self view:[sender superview] controlForTag:([sender tag] | kTPPDEControlValueMask)] withValue:choice];
//            [self setToolTip:choice fromSender:sender];
        }
    }
    else
    {
        [self rejectChoice:choice forPPDOptionKey:ppdOptionKey fromSender:sender];
    }

    return accepted;
}
- (NSInteger)tagForStringValue:(NSString *)string
{
    //subclasses must override
	return (0);
}
// This method enables you to catch the help button and determine
// the default print panel help should be shown or not.
// Returning YES will display the default help, returning NO will suppress it
// allowing you to show your own help.
-(BOOL)shouldShowHelp
{
	BOOL showDefaultHelp = true;
	NSString *helpBookName = (NSString *)[ pdeBundle objectForInfoDictionaryKey: @"CFBundleHelpBookName" ] ;
	if(helpBookName){
		OSStatus err = AHGotoPage((CFStringRef)helpBookName, NULL, NULL);
		if(!err)
			showDefaultHelp = false;
	}
	
	return showDefaultHelp;
}
-(BOOL)enablerState:(id)sender
{
    //allow subclasses to provide custom behavior
    return [sender state];
}
-(id)view:(NSView *)view controlForTag:(NSInteger)tag
{
    view = view ?: _panelView;
    return [view viewWithTag:tag];
}
-(BOOL)isControlSupportedForTag:(NSInteger)tag
{
    BOOL isControlSupported = YES;
    return isControlSupported;
}
- (void)setControlsEnabledByToggle:(id)sender
{
    NSControl *control;
    BOOL isEnabled = [self enablerState:sender];
    BOOL isControlSupported;
    int tag=kTPPDE_EnableTagStart;
    while(1)
    {
        while((control=[self view:[sender superview] controlForTag:tag++]))
        {
            isControlSupported = [self isControlSupportedForTag:(tag -1)];
            [control setEnabled:isControlSupported ? isEnabled :NO];
        }
        if(tag & kTPPDEControlValueMask)
        {
            tag = (kTPPDE_EnableTagStart | kTPPDEControlDecrementMask);
            continue;
        }

        if((tag & kTPPDEControlStepperMask) && (tag < kTPPDEControlIncrementMask)) {
            tag = (kTPPDE_EnableTagStart | kTPPDEControlIncrementMask);
            continue;
        }
        if(tag & kTPPDEControlDecrementMask)
        {
            tag = (kTPPDE_EnableStepperTagStart | kTPPDEControlStepperMask);
            continue;
        }
        if(tag & kTPPDEControlIncrementMask)
        {
            break;
        }
        tag = (kTPPDE_EnableTagStart | kTPPDEControlValueMask);
    }
}
-(NSInteger)indexOfMenuItem:(id)control forValue:(NSString *)value
{
    NSInteger index = 0;
    if([control respondsToSelector:@selector(indexOfItemWithRepresentedObject:)])
    {
        index = [control indexOfItemWithRepresentedObject:value];
    }
    return index;
}
- (void) updateControl:(id)control withValue:(id)value
{
	if([control isKindOfClass:[NSControl class]] && [value isKindOfClass:[NSString class]])
	{
        //[control setToolTip:@""];
        if([control isKindOfClass:[NSButton class]])
        {
            BOOL isOn = (NSOrderedSame == [value ?:@"" caseInsensitiveCompare:@"True"]);
            [control setState:isOn];
            if([control tag] & kTPPDE_EnableToggleTag)
            {
                [self setControlsEnabledByToggle:control];
            }
        }
        else if([control isKindOfClass:[NSPopUpButton class]])
        {
            [control selectItemAtIndex:[self indexOfMenuItem:control forValue:value]];
        }
        else if([control isKindOfClass:[NSMatrix class]])
        {
            [control selectCellWithTag:[self tagForStringValue:value]];
        }
        else if([control respondsToSelector:@selector(setStringValue:)])
        {
            [control setStringValue:value];
            control=[self view:[control superview] controlForTag: ([control tag] ^ kTPPDEControlValueMask)];
            if([control respondsToSelector:@selector(setStringValue:)])
            {
                [control setStringValue:value];
            }
        }
	}
}
// move stored values to the UI
- (void) updateUI
{
	//update the views subview controls to reflect the current state
	NSString *key;
	NSString *value;
	NSControl *control;
	NSInteger tag = 0;
	if([self panelView])
	{
        while(1)
        {
            while (((control = [self view:nil controlForTag:(++tag)]) || (control = [self view:nil controlForTag:(++tag)])) && [control isKindOfClass:[NSControl class]])
            {
                key = [self ppdOptionKeyForTag:tag];
                value = [choiceDict objectForKey:key];
                // subclasses should override (updateControl:withValue:) with a control specific method
                [self updateControl:control withValue:value];
            }
            if(tag & kTPPDE_EnableToggleTag)
            {
                break;
            }
            tag = (kTPPDE_EnableToggleTag -1);
        }
	}

}
+(NSString *)cupsValueForAttribute:(NSString *)key forPPD:(ppd_file_t *)ppd
{
	NSString * value = nil;
    ppd_attr_t * attr;
    
    if( ppd && key )
    {
        if( (attr = ppdFindAttr(ppd, [key UTF8String], NULL)) && attr->value)
        {
            value = [NSString stringWithUTF8String:attr->value];
        }
    }
    return value;
}
//-(NSString *)ppdValueForAttribute:(NSString *)key
//{
//    NSString * value = nil;
//    
//    if( ppd && key )
//    {
//        value = [self.class cupsValueForAttribute:key forPPD:ppd];
//    }
//    return value;
//}
+(NSString *)cupsDefaultChoiceForOption:(NSString *)key forPPD:(ppd_file_t *)ppd
{
	NSString * choice = nil;
    
    if( ppd && key )
    {
        ppd_option_t * option;
        ppd_attr_t * attr;
        if( (option = ppdFindOption( ppd, [key UTF8String]) ) && (option->defchoice) )
        {
            choice = [NSString stringWithUTF8String:option->defchoice ];
        }
        else if( (attr = ppdFindAttr(ppd, [[NSString stringWithFormat:@"%@Default",key] UTF8String], NULL)) && attr->value)
        {
            choice = [NSString stringWithUTF8String:attr->value];
        }
    }
    return choice;
}
+(NSString *)cupsChoiceForOption:(NSString *)key forPPD:(ppd_file_t *)ppd
{
    NSString * value = nil;
    
    if( ppd && key )
    {
        ppd_choice_t * choice;
        if((choice = ppdFindMarkedChoice(ppd, [key UTF8String])) && choice->choice)
        {
            value = [ NSString stringWithUTF8String:choice->choice ];
        }
        else
        {
            value = [self cupsDefaultChoiceForOption:key forPPD:ppd];
        }
    }
    return value;
}
+(NSString *)cupsHasChoice:(NSString *)choiceString forOption:(NSString *)key forPPD:(ppd_file_t *)ppd
{
    NSString * value = nil;
    
    if( ppd && key && choiceString)
    {
        ppd_option_t* option;
        if( (option = ppdFindOption( ppd, [key UTF8String]) ) ) {
            ppd_choice_t * choice = ppdFindChoice(option, [choiceString UTF8String]);
            if(choice && choice->choice) {
                value = [ NSString stringWithUTF8String:choice->choice ];
            }
        }
    }
    return value;
}
-(BOOL)cupsHasChoice:(NSString *)choiceString forOption:(NSString *)key {
    return (nil != [self.class cupsHasChoice:choiceString forOption:key forPPD:ppd]);
}
-(NSString *)cupsOptionValueForKey:(NSString *)key
{
    return [[self class] cupsChoiceForOption:key forPPD:ppd];
}

// set the default stored values
- (NSString *)defaultValueForOptionKey:(NSString *)key
{
    return [[self class] cupsDefaultChoiceForOption:key forPPD:ppd];
}
- (void)setDefaults
{
	NSString * optionKey;
	NSString * choice;
    
//	NSEnumerator *optionsEnum = [[self supportedPPDOptionKeys] objectEnumerator];
    for (optionKey in self.supportedPPDOptionKeys) {
        if((choice = [self defaultValueForOptionKey:optionKey]))
		{
            [self markChoice:choice forPPDOptionKey:optionKey fromSender:nil];
		}
    }
    // add a special case for the Apple PDE controlled duplex option
    // update: the special case doesn't work
//    optionKey = PPD_KEY_OPTION_DUPLEX;
//    if((choice = [self defaultValueForOptionKey:optionKey]))
//    {
//        [pdePluginCallbackObject willChangePPDOptionKeyValue:optionKey ppdChoice: choice];
//    }
//	while (optionKey=[optionsEnum nextObject])
//	{
//        if((choice = [self defaultValueForOptionKey:optionKey]))
//		{
//            [self markChoice:choice forPPDOptionKey:optionKey fromSender:nil];
//		}
//	}
}
// action method called when a PDE control is activated
- (IBAction)sendUICommand:(id)sender
{

}
-(void)waitForJobCompletion:(int)jobID
{
    NSString * destName = self.cupsDestination;
    NSUInteger destLength = [destName lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
    char * dest_name_p = malloc(destLength);
    if (NULL == dest_name_p)
    {
        // bail out here
        db("memory allocation error");
        return;
    }
    strlcpy(dest_name_p, destName.UTF8String, destLength);
    
    BOOL * didUnloadFlag = &pdePluginDidUnload;
    
    pdeUnloadSemaphore = dispatch_semaphore_create(0);
    
    dispatch_queue_t newQ = dispatch_queue_create(NULL, NULL);
    
    dispatch_async(newQ, ^{
        
        int num_jobs;
        int job_id = jobID;
        cups_job_t *jobs;
        int i;
        ipp_jstate_t job_state;
        
        // now do wasteful polling to wait for the job to complete
        do {
            
            // check to see if the PDE has ben unloaded (pde will be nil)
            if (NULL == didUnloadFlag || YES == *didUnloadFlag)
            {
                cupsCancelJob2(CUPS_HTTP_DEFAULT, dest_name_p, job_id, 1);
                NSLog(@"bailing out of wait loop bc pde plugin unloaded.");
                dispatch_semaphore_t tempSem = pdeUnloadSemaphore;
                if(0 == dispatch_semaphore_signal(pdeUnloadSemaphore))
                {
                    // returns non-zero only if a sleeping thread is woken
                    pdeUnloadSemaphore = NULL;
                    dispatch_release(tempSem);
                    
                }
                
                free(dest_name_p);
                return;
            }
            
            //search all active jobs
            num_jobs = cupsGetJobs(&jobs, dest_name_p, 0, CUPS_WHICHJOBS_ACTIVE);
            
            job_state = 0;
            // Loop to find this job
            for (i = 0; i < num_jobs; i ++)
            {
                if (jobs[i].id == job_id)
                {
                    job_state = jobs[i].state;
                    break;
                }
            }
            // Free the job array
            cupsFreeJobs(num_jobs, jobs);
            
            //Sleep if the job is not finished
        } while ((IPP_JOB_PENDING == job_state || IPP_JOB_PROCESSING == job_state) && 0 == usleep(500000));
        
        // look in completed jobs if we failed to find it in active
        if (!job_state)
        {
            num_jobs = cupsGetJobs(&jobs, dest_name_p, 0, CUPS_WHICHJOBS_COMPLETED);
            // Loop to find this job
            for (i = 0; i < num_jobs; i ++)
            {
                if (jobs[i].id == job_id)
                {
                    job_state = jobs[i].state;
                    break;
                }
            }
            // Free the job array
            cupsFreeJobs(num_jobs, jobs);
            
        }
        
        // free up the mallocd pointer
        free(dest_name_p);
        
        // Show the current state
        //db("Cups Job %d is %@",job_id, [self stringForCupsJobState:job_state]);
        
        if(IPP_JOB_COMPLETED == job_state)
        {
            [self ppdDidUpdate];
        }
        else
        {
            [self ppdUpdateDidAbort];
        }
        
        dispatch_semaphore_t tempSem = pdeUnloadSemaphore;
        pdeUnloadSemaphore = NULL;
        dispatch_release(tempSem);
    });
    dispatch_release(newQ);
    
    
}
-(void)ppdDidUpdate
{
    // do nothing here .. subclasses must override for functionality
}
-(void)ppdUpdateDidAbort
{
    // do nothing here .. subclasses must override for functionality
}
-(int)activeJobIDForDesinationName:(NSString *)destName
{
    int num_jobs;
    int job_id = 0;
    cups_job_t *jobs;
    int i;
    ipp_jstate_t job_state = IPP_JOB_PROCESSING;
    
    num_jobs = cupsGetJobs(&jobs, destName.UTF8String, 0, CUPS_WHICHJOBS_ACTIVE);
    
    // Loop to find current job
    
    for (i = 0; i < num_jobs; i ++)
        if (jobs[i].state == job_state)
        {
            job_id = jobs[i].id;
            break;
        }
    
    /* Free the job array */
    cupsFreeJobs(num_jobs, jobs);
    return job_id;
}
// action method for a PDE control to send a printer command
- (void)sendPrinterCommands:(NSArray *)commandsArray
{
    NSString * destName = self.cupsDestination;
    
    if (pdePluginDidUnload || NULL != pdeUnloadSemaphore || [self activeJobIDForDesinationName:destName])
    {
        // do nothing if there is an active job
        //[self ppdUpdateDidAbort];
        return;
    }
    
    int job_id;
    
    NSString * commandString = [NSString stringWithFormat:@"#CUPS-COMMAND\n%@\n",[commandsArray componentsJoinedByString:@"\n"]];
    
    // Create the job
    job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, destName.UTF8String, @"Updating status ...".UTF8String, 0, NULL);
    
    // If the job is created send the commands
    if (job_id > 0)
    {
        cupsStartDocument(CUPS_HTTP_DEFAULT, destName.UTF8String, job_id, "CommandJob", CUPS_FORMAT_COMMAND, 1);
        
        cupsWriteRequestData(CUPS_HTTP_DEFAULT, commandString.UTF8String, [commandString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
        
        cupsFinishDocument(CUPS_HTTP_DEFAULT, destName.UTF8String);
    }
    
    //[self waitForJobCompletion:job_id];
    
}
- (void)dsendPrinterCommands:(NSArray *)commandsArray
{
    //send supported "n" commands to the printer
    NSString * commandString = [NSString stringWithFormat:@"#CUPS-COMMAND\n%@",[commandsArray componentsJoinedByString:@"\n"]];
    PMPrinterSendCommand([pdePluginCallbackObject PMPrinter], (CFStringRef)commandString, (CFStringRef) @"PDE Commands", NULL);
}
-(IBAction)stepperAction:(id)sender {
    if([sender isKindOfClass:ATMPPR_stepper.class]) {
        ATMPPR_stepper* stepper = sender;
        double lastVal = stepper.lastValue;
        double val = stepper.doubleValue;
        stepper.lastValue = 10.;
        stepper.doubleValue = 10.;
        NSInteger tag = [sender tag];
        tag ^= kTPPDEControlStepperMask;
        if(val > lastVal) {
            [self incrementControl:[self view:[sender superview] controlForTag:tag]];
        } else if (val < lastVal) {
            [self decrementControl:[self view:[sender superview] controlForTag:tag]];
        }
    }
}
-(IBAction)incrementControl:(id)sender
{
    NSInteger tag = [sender tag];
    if(tag & kTPPDEControlIncrementMask)
    {
        tag ^= kTPPDEControlIncrementMask;
    }
    //check if the requested value is within the control range
    id control = [self view:[sender superview] controlForTag:tag];
    if([control respondsToSelector:@selector(maxValue)]&&[control respondsToSelector:@selector(intValue)]&&[control respondsToSelector:@selector(setIntValue:)])
    {
        int newValue = [control intValue] + 1;
        if([control maxValue] >= newValue)
        {
            [control setIntValue:newValue];
            [self changePrinterSetting:control];
        }
    }

}
-(IBAction)decrementControl:(id)sender
{
    NSInteger tag = [sender tag];
    if(tag & kTPPDEControlDecrementMask)
    {
        tag ^= kTPPDEControlDecrementMask;
    }
    //check if the requested value is within the control range
    id control = [self view:[sender superview] controlForTag:tag];
    if([control respondsToSelector:@selector(minValue)]&&[control respondsToSelector:@selector(intValue)]&&[control respondsToSelector:@selector(setIntValue:)])
    {
        int newValue = [control intValue] - 1;
        if([control minValue] <= newValue)
        {
            [control setIntValue:newValue];
            [self changePrinterSetting:control];
        }
    }
    
}

// action method called when a PDE control is activated
- (IBAction)changePrinterSetting:(id)sender
{
    if(NO==[sender isKindOfClass:[NSControl class]])
    {
            // abort for unknown sender objects
            return;
    }
    
    NSString * ppdOptionKey = [self ppdOptionKeyForTag:[sender tag]];
    NSString *newChoice = [self newChoiceForSender:sender];
    
    if(kTPPDESetDefaultsTag == [sender tag])
    {
            [self setDefaults];
            [self updateUI];
    }
    else if (kTPPDE_EnableToggleTag & [sender tag])
    {
        [self setControlsEnabledByToggle:sender];
        BOOL isEnabled = [sender state];
        [self markChoice: (isEnabled ? @"True" : @"False") forPPDOptionKey:ppdOptionKey fromSender:sender];
    }
    else
    {
        [self markChoice:newChoice forPPDOptionKey:ppdOptionKey fromSender:sender ];
    }
}
- (void)controlTextDidEndEditing:(NSNotification *)aNotification
{
    if([[[aNotification object] stringValue] isEqualToString:@"-"])
    {
        [[aNotification object] setStringValue:@"0"];
    }
}
- (void)controlTextDidChange:(NSNotification *)aNotification
{
//    NSLog(@"%@",aNotification);
    [self changePrinterSetting:[aNotification object]];
}

// 2008-06-10
//void clearUsbtbContextData(usbtbTaskContext *jobContext)
//{
//    if(jobContext)  // should also validate here
//    {
//        jobContext->backchannelDataSize = 0;
//        DESTROY_CFTypeRef(jobContext->backchannelData);
//        jobContext->backchannelDataPtr = NULL;
//    }
//}
//
//// 2008-06-10
//void initializeUsbtbContext(usbtbTaskContext * jobContext)
//{
//    jobContext->backchannelDataSize = 0;
//    
//    jobContext->backchannelDataPtr = NULL;
//    
//    // NULL the CFTypeRefs
//    jobContext->deviceURIString = NULL;
//    jobContext->backchannelData = NULL;
//    
//    // clear the static command buffer
//    bzero(jobContext->printerCommandBuffer,sizeof(jobContext->printerCommandBuffer));
//    jobContext->printerCommandSize = 0;
//	
//    // set the device URI from the environment
//    char * d_uri;
//    size_t bytes; 
//    while((d_uri=getenv("DEVICE_URI")))
//    {
//        // copy the deviceURI provided by the environment into the job context
//        if ((bytes=strlcpy(jobContext->deviceURI, d_uri, sizeof(jobContext->deviceURI))) >= sizeof(jobContext->deviceURI));
//        // create a CFString version
//        else
//        {
//            jobContext->deviceURIString = CFStringCreateWithCString(kCFAllocatorDefault, jobContext->deviceURI, kCFStringEncodingUTF8);
//            break;
//        }
//        
//        CUPS_DEBUG("inadequate buffersize for device URI; needed %zd bytes, had only %ld \n",bytes,sizeof(jobContext->deviceURI));
//        break;
//    }
//	
//}
//
//#define DESTROY_CFTypeRef2(arg)      (void)((NULL == (arg)) ? NULL : (CFRelease(arg), (arg)=NULL))
//void destroyUsbtbJobContext(usbtbTaskContext * jobContext)
//{
//    DESTROY_CFTypeRef2(jobContext->deviceURIString);
//    DESTROY_CFTypeRef2(jobContext->backchannelData);
//	
//}
//
//- (void)killCurrentTask
//{
//    //usbtb requires at least two SIGINTs to stop
//    while([task isRunning])
//    {
//        [task interrupt];
//        usleep(1000);
//    }
//    [task release];
//    task = nil;
//}
//- (void)bytesAvailableForStreamIN:(NSInputStream *)aStream
//{
//	// we have data to read and send to the USB device
//	// read available data from the readStream
//	uint8_t buffer[1024];
//	NSInteger len=0;
//	len = [(NSInputStream *)aStream read:buffer maxLength:sizeof(buffer)];
//	if(0 < len)
//	{
//        
//	}
//	
//}
//- (void)streamIN:(NSInputStream *)aStream handleEvent:(NSStreamEvent)eventCode
//{
//	//    NSLog(@"new stream event with eventcode %d",eventCode);
//    switch(eventCode)
//    {
//		case kCFStreamEventHasBytesAvailable:
//			[self bytesAvailableForStreamIN:aStream];
//			break;
//			
//		case kCFStreamEventErrorOccurred:
//			// check the error
//			break;
//			
//		case kCFStreamEventEndEncountered:
//			waitForCommandCompletion = NO;
//			[streamIN close];
//			[streamIN release];
//			streamIN = nil;
//			// we're finished reading data
//			break;
//			
//		case kCFStreamEventOpenCompleted:
//			// we're ready to start
//			break;
//			
//		default: // combinations of other events
//			break;
//    }
//}
//- (void)streamERR:(NSInputStream *)aStream handleEvent:(NSStreamEvent)eventCode
//{
//    uint8_t buffer[1024];
//    NSInteger len=0;
//	//    NSLog(@"new stream event with eventcode %d",eventCode);
//    switch(eventCode)
//    {
//		case kCFStreamEventHasBytesAvailable:
//			
//			// we have data to read and send to the USB device
//			// read available data from the readStream
//			len = [(NSInputStream *)aStream read:buffer maxLength:sizeof(buffer)];
//			if(0 < len)
//			{
//				NSString *errString = [NSString stringWithCString:(const char *)buffer maxLength:(NSUInteger)len encoding:NSUTF8StringEncoding];
//				NSLog(@"backend err = %@",errString);
//				//            NSRange verRange=[errString rangeOfString:[NSString stringWithFormat:@"successful transfer complete; read "]];
//				//            NSRange onlineRange=[errString rangeOfString:@"STATE: -connecting-to-device"];
//				NSRange offlineRange=[errString rangeOfString:@"STATE: +offline-error"];
//				if(0 != offlineRange.length )
//				{
//					//the device is offline
//					//do a modal alert or something someday
//					
//					[self killCurrentTask];
//					
//				}
//			}
//			
//			break;
//			
//			case kCFStreamEventErrorOccurred:
//			// check the error
//			break;
//			
//			case kCFStreamEventEndEncountered:
//			waitForCommandCompletion = NO;
//			[streamERR close];
//			[streamERR release];
//			streamERR = nil;
//			// we're finished reading data
//			break;
//			
//			case kCFStreamEventOpenCompleted:
//			// we're ready to start
//			break;
//			
//			default: // combinations of other events
//			break;
//    }
//	// at this point the task should either be finishing or blocking wating for the device
//	int check=0;
//	while (waitForCommandCompletion && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]])
//	{
//		if(10 < ++check)
//		{
//			//usbtb requires two SIGINTs to stop
//			[task interrupt];
//			[task interrupt];
//			break;
//		}
//	}
//	
//	
//}
//- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
//{
//    if([aStream isEqual:streamIN])
//    {
//        [self streamIN:(NSInputStream *)aStream handleEvent:eventCode];
//    }
//    else if([aStream isEqual:streamERR])
//    {
//        [self streamERR:(NSInputStream *)aStream handleEvent:eventCode];
//    }
//}
//
//- (int) performTaskwithUsbtbWithArgs:(NSArray *) args andContext:(usbtbTaskContext *)jobContext
//{
//	NSLog( @"Inside performTaskwithUsbtbWithArgs..." );
//	
//	
//    if(streamERR || streamIN)
//    {
//        CUPS_DEBUG("trying to launch a task with one already in progress ... exiting (-1)\n");
//        return (-1);
//    }
//    waitForCommandCompletion = 1;
//    if(nil == args)
//    {
//        CUPS_DEBUG("error ... passing nil args into %s\n",__func__);
//        return 1;
//    }
//    if(jobContext->backchannelData)
//    {
//		//        CUPS_DEBUG("warning you are entering %s with an unreleased cfdata object in the context and it may leak.\n",__func__);
//        clearUsbtbContextData(jobContext);
//    }
//    // generalized call to usbtb backend
//    NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
//    if (nil == pool)
//    {
//        CUPS_DEBUG("failed to allocate pool\n");
//        return 1;
//    }
//	
//    NSFileManager *fMan = [NSFileManager defaultManager];
//	//    NSString *cwd = [fMan currentDirectoryPath];
//	//    CUPS_DEBUG("current working directory = %s\n",[cwd UTF8String]);
//	//    NSBundle *ourBundle = [NSBundle bundleWithIdentifier:@"com.triprism.print.cups"];
//	//    ourBundle = [NSBundle bundleWithPath:@"/Library/Printers/TriPrism.bundle/"];
//    
//    NSString *usbtbPath = [pdeBundle pathForAuxiliaryExecutable:@"usbtb"];
//    if(nil == usbtbPath)
//    {
//        usbtbPath = [pdeBundle bundlePath];
//        usbtbPath = [[usbtbPath stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
//        usbtbPath = [usbtbPath stringByAppendingPathComponent:@"cups/backend/usbtb"];
//    }
//    if(NO == [fMan isExecutableFileAtPath:usbtbPath])
//    {
//        CUPS_DEBUG("no executable at %s\n",[usbtbPath UTF8String]);
//        return 1;
//    }
//	else
//	{
//        CUPS_DEBUG("Found Executable at %s\n",[usbtbPath UTF8String]);
//	}
//    int responseLength=0;    
//    NSPipe *pipe2= [NSPipe pipe];
//    NSPipe *pipe1= [NSPipe pipe];
//    NSPipe *pipe0= nil;
//    NSFileHandle *readHandle = nil;
//    NSFileHandle *writeHandle = nil;
//    NSFileHandle *errHandle = nil;
//    
//    BOOL willWrite = NO;
//    
//    //check if we will write
//    if(0 < jobContext->printerCommandSize)
//    {
//        willWrite = YES;
//        pipe0=[NSPipe pipe];
//        writeHandle=[pipe0 fileHandleForWriting];
//    }
//    task = [[NSTask alloc] init];
//    if(task && pipe1 && (!willWrite || pipe0))
//    {
//        [task setLaunchPath:usbtbPath];
//        
//        [task setStandardError:pipe2];
//        errHandle = [pipe2 fileHandleForReading];
//        if(args && [args count] > 0)
//        {
//            [task setArguments:args];
//        }
//        [task setStandardOutput:pipe1];
//        readHandle=[pipe1 fileHandleForReading];
//        if(pipe0)
//        {
//            [task setStandardInput:pipe0];
//        }
//        
//        int fd = [readHandle fileDescriptor];
//        streamIN = [[NSInputStream alloc] initWithFileAtPath:[NSString stringWithFormat:@"/dev/fd/%d",fd]];
//        [streamIN scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//        [streamIN open];
//        [streamIN setDelegate:(id)self];
//
//        fd = [errHandle fileDescriptor];
//        streamERR = [[NSInputStream alloc] initWithFileAtPath:[NSString stringWithFormat:@"/dev/fd/%d",fd]];
//        [streamERR scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//        [streamERR open];
//        [streamERR setDelegate:(id)self];
//
//        [task launch];
//        usleep(10000);
//        
//
//        if([task isRunning] &&  0 < jobContext->printerCommandSize)
//        {
//            int csize = jobContext->printerCommandSize;
//            if(sizeof(jobContext->printerCommandBuffer) < csize)
//            {
//                CUPS_DEBUG("Bad printer command size -- exceeds command buffer allocation.\n");
//            }
//            else
//            {
//                CUPS_DEBUG("writing %d bytes to the usbtb backend.\n",jobContext->printerCommandSize);
//#if __LP64__ 
//#else
//                @try {
//                    [writeHandle writeData:[NSData dataWithBytes:jobContext->printerCommandBuffer length:jobContext->printerCommandSize]];
//                }
//                @catch (NSException * e) {
//                    ;
//                    
//                }
//                @finally {
//                    ;
//                }
//#endif
//            }
//        }
//		
//        [writeHandle closeFile];
//		
//		
//    }
//    [pool release];
//    return responseLength;
//}
//- (int) performUtilityTaskWithUsbtbTaskContext:(usbtbTaskContext *)jobContext
//{
//	NSLog ( @"inside performUtilityTaskWithUsbtbTaskContext" );
//	
//    return [self performTaskwithUsbtbWithArgs:[NSArray arrayWithObjects:@"--utility",[self printerDeviceURI],@"-",nil] andContext:jobContext];
//}

#pragma mark -- PDEPanel unused methods

#if PDE_SUPPORTS_NON_PPD_SETTINGS
/*	
	The purpose of getNonPPDSettingsFromPrintSettings and writeNonPPDSettingsToPrintSettings is
	to show how in a Cocoa PDE to read values from the print settings and how to write them out.

	Missing from this code is setting a default value in case getNonPPDSettingsFromPrintSettings is not called, 
	handling summary information, etc.  

*/

- (void) getNonPPDSettingsFromPrintSettings
{
	// If this PDE handled any settings other than PPD option settings, this would
	// be the place to get those settings from the PMPrintSettings using PMPrintSettingsGetValue. 
	
	PMPrintSettings settings = [pdePluginCallbackObject printSettings];
	if(settings){
	    OSStatus err = PMPrintSettingsGetValue(settings, (CFStringRef)kOurSettingKey, (CFTypeRef *)&ourSettingValue);
	    if(err){
			// No setting for kOurSettingKey in ticket. Use the default value.
			ourSettingValue = kOurSettingDefaultCFStringValue;
			err = noErr;
	    }	
	}
}

- (void) writeNonPPDSettingsToPrintSettings
{
	// If this PDE handled any settings other than PPD option settings, this would
	// be the place to save those settings to the PMPrintSettings using PMPrintSettingsSetValue. 
	
	PMPrintSettings settings = [pdePluginCallbackObject printSettings];
	if(settings){
	    OSStatus err = PMPrintSettingsSetValue(settings, (CFStringRef)kOurSettingKey, ourSettingValue, kPMUnlocked);
		// do appropriate thing when there is an error. Here we write it
		if(err)
			NSLog(@"TP PDE got an error %d setting the value for %@ !", err, kOurSettingKey);
	}
}

#endif		/* PDE_SUPPORTS_NON_PPD_SETTINGS */

#pragma mark -- PDEPanel required methods
// the PDE panel is about to be shown
- (void)willShow
{
	// the panel is about to be shown ... do any "quick" but lazy inits here
//    NSView * sv;
//    while(sv != (sv = _panelView.superview))
//    {
//        db("superview %@ with subviews:%@",sv,sv.subviews);
//    }
	[self updateUI];
}		

- (BOOL)shouldHide
{
	// always return YES unless there is a incompatible selection requiring user action
	return YES;
}

// set UI/stored values to the print settings
- (BOOL)saveValuesAndReturnError:(NSError **)error
{
	if(!useConstraintHandling){
		// If using constraint handling, the PPD option choices are handled for us so in that
		// case there is no need to save anything for PPD options. If this code is not
		// running on 10.5 then we need to write the PPD settings in a way that is compatible
		// with Tiger.
	
		[ self writePPDSettingsToPrintSettingsOnTiger ];	
	}

	return YES;
}
-(id)printSystemValueForKey:(NSString *)key
{
    NSString * value = nil;
    if( key )
    {
        if( ppd && NULL != &ppdFindMarkedChoice )
        {
            ppd_choice_t * currentChoice = ppdFindMarkedChoice( ppd, [key UTF8String] );
            if( currentChoice && currentChoice->choice )
            {
                value = [NSString stringWithUTF8String:currentChoice->choice];
            }
        }
        if( nil == value )
        {
            PMPrintSettings settings = (PMPrintSettings)[pdePluginCallbackObject printSettings];
            if( NULL != settings )
            {
                PMPrintSettingsGetValue( settings, (CFStringRef)key, (CFTypeRef *)&value );
            }
        }
    }
    return value;
}
// read the new settings values and update our internal PDE's settings accordingly
- (BOOL)restoreValuesAndReturnError:(NSError **)error
{	
    if(useConstraintHandling)
    {
        // In this case, settings corresponding to PPD options are maintained by the implementation and do not come from 
        // the print settings but instead from the cups ppd calls. The currently marked choice is the setting for the
        // PPD option(s).
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        ppd_choice_t * ppdChoice;
        NSString *choice=nil;
        PMPrintSettings settings = (PMPrintSettings)[pdePluginCallbackObject printSettings];
        NSEnumerator *optionsEnum = [[self supportedPPDOptionKeys] objectEnumerator];
        NSString * key;
        while (ppd && ( key=[optionsEnum nextObject] ) )
        {
            ppdChoice = ppdFindMarkedChoice(ppd, [key UTF8String]);
            if(ppdChoice && ppdChoice->choice)
            {
                choice = [ NSString stringWithUTF8String:ppdChoice->choice];
            }
            else if( settings && noErr == PMPrintSettingsGetValue(settings, (CFStringRef)key, (CFTypeRef *)&choice) )
            {
                // PMPrintSettingsGetValue may return invalid choices. verify the choice is valid
                ppd_option_t * opt = ppdFindOption(ppd, [key UTF8String]);
                ppdChoice = ppdFindChoice(opt, [choice UTF8String]);
                if(NULL == ppdChoice)
                {
                    choice = nil;
                }
            }
            [self setChoice:(choice ?: [self defaultValueForOptionKey:key]) forKey:key];
        }
        [pool drain];
    }
    else
    {
        [ self getPPDSettingsFromPrintSettingsOnTiger ];
        if([choiceDict count] != [[self supportedPPDOptionKeys] count])
            [self setDefaults];
    }

#if PDE_SUPPORTS_NON_PPD_SETTINGS
    [ self getNonPPDSettingsFromPrintSettings ]; 
#endif

    // Now have UI reflect the current settings.
    if(_panelView)
    {
        [self updateUI];
    }
        
    return YES;
}

// return an NSArray of PPD option keywords supported by this PDE
- (NSArray *)supportedPPDOptionKeys;
{
	// in the nib file, each widget that controls a PPD option should be enumerated with a tag
	// starting with 1. Each tag should correspond to a PPD option key in the subclass
	// implementation of ppdOptionKeyForTag:
	
	if (nil == supportedPPDOptionKeys)
	{
		int tag = 0;
		NSString *key;
		NSMutableArray *array=[NSMutableArray array];
		while ((key = [self ppdOptionKeyForTag:(++tag)]))
		{
			[array addObject:key];
		}
		supportedPPDOptionKeys = [array copy];
	}
	return supportedPPDOptionKeys;
}

// This method is called when the system has marked a new PPD option/choice. The passed
// in choice is the new setting for this PPD option.
- (void) PPDOptionKeyValueDidChange:(NSString *)option ppdChoice:(NSString *)choice
{
	if(option && choice && [[self supportedPPDOptionKeys] containsObject:option])
	{
	    [self setChoice:choice forKey:option];
        [self updateUI];
	}
}

- (NSString*)nibName
{
	return nil;
}
// return the NSView for this PDE. This method has "Get" semantics so
// this code continues to have a reference to the NSView it returns.
- (void)setPanelView:(NSView *)view
{
    if ( _panelView != view )
    {
        view = [view retain];
        [_panelView release];
        _panelView = view;
    }
}
-(void)enableCustom
{
    // available for subclasses
}
- (NSView *)panelView
{
	if (nil == _panelView)
	{
		NSString *nibName=[self nibName];
		if(nibName)
		{
			if(NO ==[NSBundle loadNibNamed:nibName owner:self])
            {
				NSLog(@"Failed to load nib named %@",nibName);
            }
            else
            {
                // localize nib strings
                [self populateMenuObjectsInView:_panelView];
                [self localizeStringsInView:_panelView andSubviews:YES resizeTofit:NO];
                [self enableTextFieldsForEditingInView:_panelView];
                [self enableCustom];
            }
		}
	}	
	return _panelView;
}

// Return the localized name for this PDE. This will be used as its title in the
// menu of printing panes. Subclasses must override this method.
- (NSString*)panelName
{
	return nil;
}

// return the kind ID for this PDE
- (NSString*)panelKind
{
	return nil;
}

// format info for Summary panel
- (NSDictionary *) summaryInfo
{
	// return an NSDict that contains the option keys as keys and the option values (choice->text) as values
	NSMutableDictionary *summaryInfoDict = [NSMutableDictionary dictionary];
	
	ppd_choice_t *choice;
	NSEnumerator *optionsEnum = [[self supportedPPDOptionKeys] objectEnumerator];
	NSString * optionKey;
	NSString * value;
	while (optionKey=[optionsEnum nextObject] )
	{
		if(useConstraintHandling)
		{
			choice = ppdFindMarkedChoice(ppd, [optionKey UTF8String]);
			if (choice && choice->text)
			{ 
				value = NSLocalizedStringFromTableInBundle([NSString stringWithUTF8String:choice->text], nil, pdeBundle, nil);
				[summaryInfoDict setObject:value forKey: [pdeBundle localizedStringForKey:optionKey value:nil table:nil]];		
			}
		}
		else
		{
			value = NSLocalizedStringFromTableInBundle([choiceDict objectForKey:optionKey], nil, pdeBundle, nil);
			[summaryInfoDict setObject:value forKey: [pdeBundle localizedStringForKey:optionKey value:nil table:nil]];
		
		}
	}

	return summaryInfoDict;
}


#pragma mark -- PDEPanel Tiger-only methods
/*   ********* Methods only needed on Tiger **************  */

- (ppd_file_t *)getPPDOnTiger
{
	ppd_file_t *currentPPD = NULL;
#if !__LP64__
		OSStatus	GetPPDRefFromSession(PMPrintSession printSession, ppd_file_t **ppd);
		OSStatus err = GetPPDRefFromSession( [ pdePluginCallbackObject printSession ], &currentPPD );
		if(err)
			currentPPD = NULL;
#endif
	return currentPPD;
}

- (void) usePPDSettingsFromPrintSettingsOnTiger:(id)nilToRead
{
// do nothing on 64-bit implementations where these PMSession functions are not supported
#if !__LP64__
//	PMTicketRef printSettings = NULL;
//	NSMutableDictionary	*ppdDict = nil;
//	// obtain the print setings ticket from the session
//	OSStatus err = PMSessionGetTicketFromSession([pdePluginCallbackObject printSession], kPDE_PMPrintSettingsRef, &printSettings);
//	if (noErr == err)
//	{
//		// obtain the current PPD keyword dictionary
//		err = PMTicketGetPPDDict(printSettings, kPMTopLevel, kPMTopLevel, (CFMutableDictionaryRef *)&ppdDict);
//		if (noErr == err  && nil != ppdDict)
//		{
//			if(nil == nilToRead)
//			{
//				if(nil==choiceDict)
//				{
//					choiceDict=[[NSMutableDictionary dictionary] retain];
//				}
//				[choiceDict setValuesForKeysWithDictionary:[ppdDict dictionaryWithValuesForKeys:[self supportedPPDOptionKeys]]];
//			}
//			else
//			{
//				// Save the current user choices
//				[ppdDict setValuesForKeysWithDictionary:choiceDict];
//			}
//		}
//	}
#endif
}
- (void) getPPDSettingsFromPrintSettingsOnTiger
{
	[self usePPDSettingsFromPrintSettingsOnTiger:nil];

}

- (void) writePPDSettingsToPrintSettingsOnTiger
{
	[self usePPDSettingsFromPrintSettingsOnTiger:self];
}

@end

//@implementation ATMPPRMatrixPDENoritsu
//
//- (void) updateControl:(id)control withValue:(id)value
//{
//	if(control && value && [control isKindOfClass:[NSMatrix class]])
//	{
//		[control selectCellWithTag:[self tagForStringValue:value]];
//		[control setToolTip:@""];
//	}
//}
//- (id)newChoiceForSender:(id)sender
//{
//	NSInteger tag = 0;
//	if([sender respondsToSelector:@selector(selectedCell)])
//	{
//		tag = [[sender selectedCell] tag];
//	}
//	else if([sender respondsToSelector:@selector(tag)])
//	{
//		tag = [sender tag];
//	}
//	return [self ppdChoiceForTag:tag];
//}
//-(void)setToolTip:(id)value fromSender:(id)sender
//{
//	[sender setToolTip:@""];
//}
//
//
//@end

@implementation ATMPPR_stepper



@end
